<template>
  <div id="app">
    <fieldset>
      <legend>设置</legend>
      <label for="number">烙饼张数</label>
      <select style="margin-right:50px" id="number" @change="numberChange">
        <option v-for="index in numbers" :key="index">{{index}}</option>
      </select>
      <button @click="shuffle">洗牌</button>
      <button @click="reset" style="margin-right:30px">重置</button>
      <button @click="sort" :disabled="cannotSort">开始排序</button>
    </fieldset>
    <canvas
      ref="canvas"
      style="width:960px;height:540px;margin:auto;margin-top:100px;"
      width="1920"
      height="1080"
    />
  </div>
</template>

<script>
import * as State from "./state";
import pancakeSorting from "./algorithms/pancake_sorting";
import animejs from "animejs";
const start = 3;
const total = 78;
// const verticalMargin = 3;
// const pancakeHeight = 10;
// const pancakeWidthUnit = 12;

let verticalMargin;
let pancakeHeight;
let pancakeWidthUnit;

const deactiveColor = "orange";
const activeColor = "red";
class Pancake {
  number = 0;
  color = "";
}
class PancakeGroup {
  items = [];
  centerX = 0;
  centerY = 0;
  rotate = 0;
  draw(ctx) {
    ctx.save();
    ctx.translate(this.centerX, this.centerY);
    ctx.rotate(this.rotate);
    const len = this.items.length;
    let startY;
    if (len % 2 === 0) {
      startY =
        -verticalMargin / 2 -
        (len / 2) * pancakeHeight -
        (len / 2 - 1) * verticalMargin;
    } else {
      startY =
        -pancakeHeight / 2 -
        Math.floor(len / 2) * (pancakeHeight + verticalMargin);
    }
    for (let i = 0; i < len; i++) {
      ctx.fillStyle = this.items[i].color;
      const width = this.items[i].number * pancakeWidthUnit;
      ctx.fillRect(
        -width / 2,
        startY + i * (pancakeHeight + verticalMargin),
        width,
        pancakeHeight
      );
    }
    ctx.restore();
  }
}
function resize(count) {
  if (count < 9) {
    verticalMargin = 40;
    pancakeHeight = 100;
    pancakeWidthUnit = 200;
  } else if (count < 15) {
    verticalMargin = 20;
    pancakeHeight = 60;
    pancakeWidthUnit = 100;
  } else if (count < 23) {
    verticalMargin = 10;
    pancakeHeight = 40;
    pancakeWidthUnit = 50;
  } else if(count<33){
    verticalMargin = 5;
    pancakeHeight = 20;
    pancakeWidthUnit = 26;
  }else if(count<55){
    verticalMargin = 4;
    pancakeHeight = 15;
    pancakeWidthUnit = 20;
  }else{
    verticalMargin = 3;
    pancakeHeight = 10;
    pancakeWidthUnit = 12;
  }
}
resize(3);
export default {
  name: "App",
  data() {
    return {
      num: start,
      arr: [],
      top: null,
      other: null,
      bottom: null,
      initGroup: null,
      originArr: [],
      result: [],
      numbers: [...Array(total)].map((_, i) => i + start),
      state: State.IDLE,
      canvas: null,
      cannotSort: false
    };
  },
  mounted() {
    this.canvas = this.$refs.canvas;
    this.shuffle();
    requestAnimationFrame(this.render);
  },
  methods: {
    initPancake() {
      this.bottom = null;
      this.top = null;
      this.other = null;
      const initGroup = new PancakeGroup();
      initGroup.centerX = this.canvas.width / 2;
      initGroup.centerY = this.canvas.height / 2;
      initGroup.rotate = 0;
      for (let i = 0; i < this.arr.length; i++) {
        const p = new Pancake();
        p.number = this.arr[i];
        p.color = deactiveColor;
        initGroup.items.push(p);
      }
      this.initGroup = initGroup;
    },
    sleep(ms) {
      return new Promise(resolve => {
        setTimeout(resolve, ms);
      });
    },
    async sort() {
      // cache
      this.cannotSort = true;
      this.state = State.SORT;
      this.originArr = Object.assign(this.arr);
      try {
        await this.sortSync();
        for (let i = 0; i < this.result.length; i++) {
          await this.sortByResult(this.result[i]);
        }
        //排好了
        const bottomGroup = new PancakeGroup();
        const count = this.arr.length;
        const top =
          (this.canvas.height -
            (this.arr.length - 1) * verticalMargin -
            this.arr.length * pancakeHeight) /
          2;
        const bottom =
          (this.canvas.height +
            (this.arr.length - 1) * verticalMargin +
            this.arr.length * pancakeHeight) /
          2;
        const centerY = (top + bottom) / 2;
        const centerX = this.canvas.width / 2;
        bottomGroup.centerX = centerX;
        bottomGroup.centerY = centerY;
        bottomGroup.rotate = 0;
        for (let i = 0; i < count; i++) {
          const p = new Pancake();
          p.number = i + 1;
          p.color = activeColor;
          bottomGroup.items.push(p);
        }
        this.bottom = bottomGroup;
      } catch (e) {
      } finally {
        this.cannotSort = false;
      }
    },
    sortByResult(result) {
      const vm = this;
      return new Promise(async (resolve, reject) => {
        try {
          {
            const bottomGroup = new PancakeGroup();
            const count = this.arr.length - result.n;
            let top =
              (this.canvas.height -
                (this.arr.length - 1) * verticalMargin -
                this.arr.length * pancakeHeight) /
              2;
            top += result.n * (pancakeHeight + verticalMargin);
            const bottom =
              (this.canvas.height +
                (this.arr.length - 1) * verticalMargin +
                this.arr.length * pancakeHeight) /
              2;
            const centerY = (top + bottom) / 2;
            const centerX = this.canvas.width / 2;
            bottomGroup.centerX = centerX;
            bottomGroup.centerY = centerY;
            bottomGroup.rotate = 0;
            for (let i = 0; i < count; i++) {
              const p = new Pancake();
              p.number = this.arr[result.n + i];
              p.color = activeColor;
              bottomGroup.items.push(p);
            }
            this.bottom = bottomGroup;
          }
          {
            const topGroup = new PancakeGroup();
            const count = result.idx + 1;
            const top =
              (this.canvas.height -
                (this.arr.length - 1) * verticalMargin -
                this.arr.length * pancakeHeight) /
              2;
            const bottom =
              top + count * pancakeHeight + (count - 1) * verticalMargin;
            const centerY = (top + bottom) / 2;
            const centerX = this.canvas.width / 2;
            topGroup.centerX = centerX;
            topGroup.centerY = centerY;
            topGroup.rotate = 0;
            for (let i = 0; i < count; i++) {
              const p = new Pancake();
              p.number = this.arr[i];
              p.color = deactiveColor;
              topGroup.items.push(p);
            }
            this.top = topGroup;
            const otherGroup = new PancakeGroup();
            const top2 = bottom + verticalMargin;
            const cnt = result.n - result.idx;
            const bottom2 =
              top2 + cnt * pancakeHeight + (cnt - 1) * verticalMargin;
            otherGroup.centerX = this.canvas.width / 2;
            otherGroup.centerY = (top2 + bottom2) / 2;
            for (let i = count; i < result.n + 1; i++) {
              const p = new Pancake();
              p.number = this.arr[i];
              p.color = deactiveColor;
              otherGroup.items.push(p);
            }
            this.other = otherGroup;
            if (result.idx > 0) {
              await this.rotateTop();
            }
          }
          this.arr = Object.assign(result.after);
          {
            const topGroup = new PancakeGroup();
            const count = result.n;
            const top =
              (this.canvas.height -
                (this.arr.length - 1) * verticalMargin -
                this.arr.length * pancakeHeight) /
              2;
            const bottom =
              top + count * pancakeHeight + (count - 1) * verticalMargin;
            const centerY = (top + bottom) / 2;
            const centerX = this.canvas.width / 2;
            topGroup.centerX = centerX;
            topGroup.centerY = centerY;
            topGroup.rotate = Math.PI;
            for (let i = 0; i < count; i++) {
              const p = new Pancake();
              p.number = this.arr[i];
              p.color = deactiveColor;
              topGroup.items.push(p);
            }
            this.top = topGroup;
            this.other = null;
            await this.rotateTop(true);
          }
        } catch (e) {
          reject();
        }
        resolve();
      });
    },
    rotateTop(flag = false) {
      const vm = this;
      return new Promise((resolve, reject) => {
        let s = [0, 2][Math.round(Math.random())];
        let e = 1;
        if (flag) {
          s = 1;
          e = [0, 2][Math.round(Math.random())];
        }
        const t = {
          value: s
        };
        animejs({
          targets: t,
          value: e,
          duration: 1000,
          update(anim) {
            if (vm.state !== State.SORT) {
              this.pause();
              reject();
            } else {
              vm.top.rotate = Math.PI * t.value;
            }
          },
          complete() {
            resolve();
          }
        }).play();
      });
    },
    sortSync() {
      return new Promise(resolve => {
        this.result = pancakeSorting(this.arr);
        resolve();
      });
    },
    shuffle() {
      this.state = State.IDLE;
      const temp = [...Array(this.num)].map((_, i) => i + 1);
      this.arr = this.shuffleArray(temp);
      this.initPancake();
    },
    shuffleArray(arr) {
      const temp = Object.assign(arr);
      const ret = [];
      while (temp.length > 0) {
        const t = Math.floor(temp.length * Math.random());
        ret.push(temp[t]);
        temp.splice(t, 1);
      }
      return ret;
    },
    reset() {
      if (this.state === State.SORT) {
        this.state = State.IDLE;
        this.arr = Object.assign(this.originArr);
        this.initPancake();
      }
      // reset
    },
    render(time) {
      const canvas = this.canvas;
      const ctx = canvas.getContext("2d");
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      if (this.state === State.IDLE) {
        if (this.initGroup) {
          this.initGroup.draw(ctx);
        }
      } else if (this.state === State.SORT) {
        if (this.top) {
          this.top.draw(ctx);
        }
        if (this.other) {
          this.other.draw(ctx);
        }
        if (this.bottom) {
          this.bottom.draw(ctx);
        }
      }
      requestAnimationFrame(this.render);
    },
    numberChange(e) {
      this.num = parseInt(e.target.value);
      resize(this.num);
      this.state = State.IDLE;
      this.shuffle();
    }
  }
};
</script>

<style>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  height: 100%;
  background-color: #7d90c1;
}
</style>
